/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
#include <X11/Intrinsic.h>
#include <X11/Xatom.h>
#include <X11/StringDefs.h>
#include <X11/Core.h>
#include <X11/Shell.h>
#include <X11/keysym.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <stdio.h> 
#include <ctype.h>
#include <sys/file.h>

#define SM_CLASS extern
#include "smlib.h"
#include "smio.h"
#include "smxaw.h"

#ifdef void
#undef void /* Sun cc can grok it even if it's not ANSI-C */
#endif

static SM_SOFTKEY 	smio_current_skey = NULL;
static int		smxaw_form_realised = 0;
extern int		smio_insert_state;
static XtAppContext 	appctx;
static Display 		*dpy;
static Widget 		top, form0, bottomButtons, userScreen, topButtons;
static Widget 		key[8], currentWidget = NULL;
SM_FIELD		currentField;
static Widget 		MsgWidget, InsWidget = 0, MetaWidget;
static int		errorFlag;
static Widget		promptShell, selectShell;
static Widget		promptCurrentWidgetStore;
static int		font_width, font_height;
static int		smxaw_mouse_col, smxaw_mouse_row, smxaw_button;

static int		smxaw_row = 0, smxaw_col = 0;
static f_ptr		smxaw_got_a_command;
static Widget		current_fld = NULL;

static int		top_of_wstack 	= 0;
static int		cursor_valid 	= TRUE;
static int		meta_flag 	= 0;
static int 		pushed_char	= 0;
static int		pushed_command	= 0;

#define MAX_FIELD_LEN 100
#define NUM_LINES_ON_FORM 24
#define FORM_WIDTH 80 		/* How big to create userScreen */

#define BACKGROUND 	0
#define FIELD 		1
static struct element {
    int		type; /* FIELD or BACKGROUND */
    int		row;
    int		col;
    int		len;
    int		enhance; /* FIELD only */
    void	*ptr;
} **displayList = NULL;
typedef struct element *ELEMENT;

static String fallback_resources[] = {
"!*font:				courier",
"!",
"! File Menu",
"!",
"*fileButton.label:		File ...",
"*fileButton.internalWidth:	0",
"*fileButton.internalHeight:	0",
"!",
"*Load.label:			Load         M-^l",
"*Store.label:			Store        M-^s",
"*StoreAs.label:			Store As ... M-^a",
"*Clear.label:			Clear File   M-^c",
"*Version.label:			Version      ^v",
"*Quit.label:			Quit         M-Z",
"*Print.label:			Print",
"!",
"! Edit Menu",
"!",
"*editButton.label:		Edit ...",
"*editButton.internalWidth:	0",
"*editButton.internalHeight:	0",
"!",
"*CopyRecord.label:              Copy Record        M-C",
"*PasteRecord.label:             Paste Record       M-Y",
"*InsertRecord.label:            Insert Record      M-I",
"*DeleteRecord.label:            Delete Record      M-D",
"*ClearToEnd.label:              Clear To End       ^k",
"*ClearRecord.label:             Clear Record       M-k",
"*TransposeLetters.label:        Transpose Letters  ^t",
"*TransposeWords.label:          Transpose Words    M-t",
"*ToggleCase.label:              Toggle Case        ^u",
"*FindFirst.label:               Find First         M-s",
"*FindLast.label:                Find Last          M-l",
"*SearchReplace.label:           Search and Replace M-r",
"*ReplaceAll.label:              Replace All        M-R",
"*Again.label:                   Again              M-S",
"*GoToRecord.label:              Go To Record #     M-g",
"*Sort.label:                    Sort               M-x",
"*Where.label:                   Where              M-w",
"*EditMaster.label:              Edit Master        M-L",
"*ChangeRecordType.label:        Change Record Type M-T",
"!",
"*errorContinue.label:		Continue",
"*AsciiSink.foreground:		White",
"*AsciiSink.background:		Black",
"!",
"*Prompt*label.justify:		left",
"*Prompt*label.internalHeight:	0",
"*Prompt*label.internalWidth:	0",
"*Prompt*label.bottomMargin:	0",
"*Prompt*label.topMargin:	0",
"*Prompt*label.rightMargin:	0",
"*Prompt*value.internalHeight:	0	",
"*Prompt*value.internalWidth:	0	",
"*Prompt*value.bottomMargin:	0",
"*Prompt*value.topMargin:	0",
"*Prompt*value.leftMargin:	0",
"*Prompt*value.rightMargin:	0",
"!",
"! Global translations",
"!",
"*translations:			#override \n\
<Key>:	ProcessKey()",
"!",
"! Text Widgets i.e. SCRMGR fields",
"!",
"*Text.borderWidth:		0",
"*Text.internalHeight: 		0",
"*Text.internalWidth:   		0",
"*Text.bottomMargin:        	0",
"*Text.topMargin:           	0",
"*Text.leftMargin:          	0",
"*Text.rightMargin:         	0         ",
"*Text.editType:            	textEdit",
"*Text.displayCaret:            	FALSE",
"*Text.justify:			left",
"!*Text.cursor:			xterm",
"*Text.translations:		#override \n\
<Key>:		ProcessKey() \n\
<Btn1Down>: 	ProcessB1() select-start()",
"!",
"! Label Widgets i.e. SCRMGR background and Message line",
"!",
"*Label.borderWidth:		0",
"*Label.internalHeight: 		0",
"*Label.internalWidth:   	0",
"*Label.justify:			left",
"!",
"! Command Widget - SCRMGR buttons and Xaw pushbuttons",
"!",
"*Command.borderWidth:		1",
"*Command.internalHeight: 	0",
"*Command.internalWidth:   	0",
"*Command.justify:		left",
    NULL
};

void smxaw_update_insert()
{
    if (smio_insert_state)
	XtVaSetValues(InsWidget, XtNlabel, "I", NULL);
    else
	XtVaSetValues(InsWidget, XtNlabel, "O", NULL);
}

static void unset_cursor()
{
    if (currentWidget) {
	XtVaSetValues(currentWidget, XtNdisplayCaret, 0, NULL);
	currentWidget = NULL;
    }
}

static Widget field_to_widget ARG1(SM_FIELD, field)
{
    int		offset;
    SM_SCREEN	screen;
    Widget	retval = NULL;

    if (field && field->xaw_widgetp) {
	offset = 0;
	screen = field->parent;
	if (screen->scrolling && (field->row >= screen->scrolling - 1))
	    offset = field->row - (screen->scrolling - 1);
	retval = ((Widget *)(field->xaw_widgetp))[offset];
    }
    return(retval);
}

static void add_widget_to_field ARG2(SM_FIELD, field, Widget, new_widget)
{
    INDEX	new = MAX_INDEX;
    Widget	*w_ptr = NULL;

    if (field) {
	w_ptr = field->xaw_widgetp;
	w_ptr = (Widget *) sm_increase_size((SM_DATA) w_ptr, &new);
	w_ptr[new] = new_widget;
	field->xaw_widgetp = w_ptr;
    }
}

void smxaw_set_cursor ARG2(SM_FIELD, field, int, pos)
{
    Widget	w = field_to_widget(field);

    if (w) {
	if (currentWidget)
	    unset_cursor();
	currentField = field;
	currentWidget = w;
	XtVaSetValues(currentWidget, XtNdisplayCaret, 1, NULL);
	XawTextSetInsertionPoint(currentWidget, pos);
    }
}

#define SM_XAW_ACTION(f) static void f ARG4(Widget, 	w, \
					    XEvent *,	event, \
					    String *, 	params, \
					    Cardinal, 	cardinal)

#define SM_ACTION_FN(f, val) \
    static void f() { unset_cursor(); smxaw_got_a_command(val); }
SM_ACTION_FN(f1, SM_META('1'))
SM_ACTION_FN(f2, SM_META('2'))
SM_ACTION_FN(f3, SM_META('3'))
SM_ACTION_FN(f4, SM_META('4'))
SM_ACTION_FN(f5, SM_META('5'))
SM_ACTION_FN(f6, SM_META('6'))
SM_ACTION_FN(f7, SM_META('7'))
SM_ACTION_FN(f8, SM_META('8'))

void InsertCallback()
{
    smio_insert_state = !smio_insert_state;
    smxaw_update_insert();
}

SM_XAW_ACTION(ProcessKey)
{
#define BUFLEN 20
    int			length, n, buf_printable, retval = 0, command;
    KeySym		keySym;
    XComposeStatus	composeStatus;
    char		buf[BUFLEN];

    switch (event->type) {
    case ButtonPress:
	printf("ButtonPress\n");
	break;
    case KeyPress:
	length = XLookupString((XKeyEvent *)event, buf, BUFLEN, &keySym, &composeStatus);
	buf[length] = 0;
	buf_printable = 1;
	for (n = 0; n < length; n++)
	    if ((buf[n] < ' ') || (buf[n] > '~'))
		buf_printable = 0;

	printf("KeyPress <%d> <0x%x> <0%o> buf=[%s] ", 
	       keySym, keySym, keySym, ((length > 0) && buf_printable)? buf: "");
	if (event->xkey.state & Mod1Mask)
	    printf("Meta-");
	if (event->xkey.state & ControlMask)
	    printf("Ctrl-");
	if ((keySym >= ' ') && (keySym <= '~') && (length > 0))
	    printf("[%c] \n", keySym);

	n = keySym - XK_F1;
	if ((n >= 0) && (n <= 12)) {
	    n = '1' + n;
	    retval = SM_META(n);
	} else if ((keySym >= ' ') && (keySym <= '~') && (length > 0)) {
	    n = keySym;
	    if (event->xkey.state & ControlMask)
		n = SM_CTRL(n);
	    retval = n;
	} else {
	    switch (keySym) {
	    case XK_Linefeed:
	    case XK_Return: 	retval = SM_CTRL('J'); 	break;
	    case XK_BackSpace: 	retval = SM_CTRL('H');	break;
	    case XK_Delete:	retval = SM_CTRL('D');	break;
	    case XK_Escape: 	retval = SM_CTRL('['); 	break;
	    case XK_Up: 	retval = SM_CTRL('P'); 	break;
	    case XK_Down: 	retval = SM_CTRL('N'); 	break;
	    case XK_Right: 	retval = SM_CTRL('F'); 	break;
	    case XK_Left: 	retval = SM_CTRL('B'); 	break;
	    case XK_R9:
	    case XK_Prior: 	retval = SM_META('P'); 	break;
	    case XK_R15:
	    case XK_Next: 	retval = SM_META('N'); 	break;
	    case XK_R7:
	    case XK_Home:
	    case XK_Begin: 	retval = SM_CTRL('A'); 	break;
	    case XK_End: 	retval = SM_CTRL('E'); 	break;
	    case XK_Find: 	retval = SM_META('s'); 	break;
	    case XK_Help: 	retval = SM_META('H'); 	break;
	    case XK_Insert: 	retval = SM_META('i'); 	break;
	    case XK_Tab: 	retval = SM_CTRL('I'); 	break;
	    default:		
		break;
	    }
	}
	break;
    default:
	break;
    }

    if (retval) {
	printf("Mapped to %d\n", retval);
	if (event->xkey.state & Mod1Mask)
	    retval = SM_META(retval);
	if (retval > 0) {
	    unset_cursor();
	    smxaw_got_a_command(retval);
	}
    } else
	printf("Ignored\n");
}
    
SM_XAW_ACTION(ProcessB1)
{
    int		x, y;
    Position	x0, y0;
    Widget	parent = XtParent(w);

    if (parent == userScreen) {
	XtVaGetValues(w, XtNx, &x0, XtNy, &y0, NULL);
	x = ((XButtonEvent *)event)->x + x0;
	y = ((XButtonEvent *)event)->y + y0;

	smxaw_mouse_col = x / font_width;
	smxaw_mouse_row = y / font_height;
	smxaw_button = 1;
	printf("ProcessB1 (%d, %d)\n", smxaw_mouse_col, smxaw_mouse_row);
	smxaw_got_a_command(SM_MOUSE_EVENT_K);
    }
}

static Widget create_field ARG5(char *,	name,
				char *, label, 
				Widget, parent, 
				Widget,	above,
				Widget, left)
{
    Widget w;
    
    w = XtVaCreateManagedWidget(name, asciiTextWidgetClass, parent,
				XtNfromVert,     above,
				XtNfromHoriz,	 left,
				/* XtNeditType,	 XawtextEdit,*/
				XtNstring,	 label,
				XtNlength,	 strlen(label),
				XtNwidth,	 strlen(label)*font_width,
				NULL);
#if 0
    XtVaSetValues(w, 
		  XtNdisplayCaret, 0, /* doesn't work if placed in vararg list!*/
		  NULL);
#endif
    return(w);
}

static Widget create_label ARG4(char *, label, 
				Widget, parent, 
				Widget, above, 
				Widget, left)
{
    return(XtVaCreateManagedWidget(label, labelWidgetClass, parent,
				   XtNfromVert,     	above,
				   XtNfromHoriz,	left,
				   XtNlength,		strlen(label),
				   NULL));
}

static void SetInSensitive()
{
    XtSetSensitive(userScreen, FALSE);
    XtSetSensitive(topButtons, FALSE);
}

static void SetSensitive()
{
    XtSetSensitive(userScreen, TRUE);
    XtSetSensitive(topButtons, TRUE);
}

static void smxaw_warp_mouse ARG1(Widget, w)
{
    XWarpPointer(dpy, (Window) NULL, XtWindow(w), 0, 0, 0, 0, 10, 30);
}

void smxaw_select_done()
{
    XtPopdown(selectShell);
    SetSensitive();
    if (selectShell)
	XtDestroyWidget(selectShell);
    selectShell = NULL;
}

void smxaw_do_selection ARG2(SM_SCREEN,		screen, 
			     INDEX *,		index)
{
    smxaw_select_done();
    
    if (screen->temp_storage != 'F') {
	unlink(screen->filename);
	free(screen->filename);
	screen->filename = NULL;
    }
}

static void SelectDone()   
{ 
    smxaw_got_a_command(SM_CTRL('J')); 
}

static void SelectCancel() 
{ 
    smxaw_got_a_command(SM_CTRL('C')); 
}

void smxaw_setup_selection ARG3(SM_SCREEN, 	screen, 
				INDEX *,		index, 
				SM_SOFTKEY, 	skey)
{
    XEvent	event;
    Widget 	selectCancel, selectForm, selectDone;
    Widget 	selectLabel1, selectLabel2, selectField;
    Position	x, y;
    CHAR	*filename, *filenamebuf = alloca(MAXPATHLEN);
    FILE	*file;
    static XawTextSelectType	clicks[2];

    if (screen->temp_storage != 'F') { /* then write it to a file */
	CHAR	*buf;

	sm_mktemp(filenamebuf, "XXXXXX");
	file = fopen(filenamebuf, "w");
	smdb_seek(screen, (INDEX) 0);
	do {
	    buf = smdb_get_current(screen);
	    if (buf) {
		fputs(buf, file);
		fputc('\n', file);
	    }
	} while (smdb_read_next(screen));
	fclose(file);
	SM_ASSIGN(screen->filename, filenamebuf);
    }

    selectShell = XtVaCreatePopupShell("Selection",
				       transientShellWidgetClass, top, 
				       XtNallowShellResize,	TRUE,
				       NULL);
    selectForm = XtVaCreateManagedWidget("selectForm", formWidgetClass,
					 selectShell, 
					 NULL);
    selectDone = XtVaCreateManagedWidget("selectDone", 
					 commandWidgetClass, selectForm, 
					 NULL);
    selectCancel = XtVaCreateManagedWidget("selectCancel", commandWidgetClass,
					   selectForm, 
					   XtNfromHoriz,	selectDone,
					   NULL);
    clicks[0] = XawselectLine;
    clicks[1] = XawselectNull;
    selectField = XtVaCreateManagedWidget("selectValue", asciiTextWidgetClass,
					  selectForm, 
					  XtNfromVert,    selectDone,
					  XtNeditType,    XawtextEdit,
					  XtNtype,        XawAsciiFile,
					  XtNstring,	  screen->filename,
					  XtNselectTypes, clicks,
					  NULL);

    XtAddCallback(selectDone,   XtNcallback, SelectDone,   NULL);
    XtAddCallback(selectCancel, XtNcallback, SelectCancel, NULL);

    SetInSensitive();

    /* Locate window */
    XtTranslateCoords(top, 
		      (Position) 0, 
		      (Position) 0, 
		      &x,
		      &y);
    XtVaSetValues(selectShell, XtNx, x, XtNy, y, NULL);

    XtPopup(selectShell, XtGrabNonexclusive);
    smxaw_warp_mouse(selectDone);
}

void smxaw_prompt_done() /* called from smio_prompt_done() */
{
    XtPopdown(promptShell);
    SetSensitive();
    currentWidget = promptCurrentWidgetStore;
    if (sm_prompt_fld->xaw_widgetp)
	free(sm_prompt_fld->xaw_widgetp);
    sm_prompt_fld->xaw_widgetp = NULL;
    if (promptShell)
	XtDestroyWidget(promptShell);
    promptShell = NULL;
}

static void PromptDone()   { smxaw_got_a_command(SM_CTRL('J')); }
static void PromptCancel() { smxaw_got_a_command(SM_CTRL('C')); }

int smxaw_setup_prompt ARG3(CHAR *, s1, CHAR *, s2, CHAR *, default_result)
{
    XEvent	event;
    Widget 	promptCancel, promptForm, promptDone;
    Widget 	promptLabel1, promptLabel2, promptField;
    Position	x, y;

    promptShell = XtVaCreatePopupShell("Prompt",
				       transientShellWidgetClass, top, 
				       XtNallowShellResize,	TRUE,
				       NULL);
    promptForm = XtVaCreateManagedWidget("promptForm", formWidgetClass,
					 promptShell, 
					 NULL);
    promptLabel1 = XtVaCreateManagedWidget("label", labelWidgetClass, 
					   promptForm, 
					   XtNlabel, 	s1,
					   NULL);
    if (s2 && strlen(s2))
	promptLabel2 = XtVaCreateManagedWidget("label2", labelWidgetClass, 
					       promptForm, 
					       XtNlabel, 	s2,
					       XtNfromVert,	promptLabel1,
					       NULL);
    else
	promptLabel2 = promptLabel1;

    promptField = XtVaCreateManagedWidget("value", asciiTextWidgetClass, 
					  promptForm, 
					  XtNstring, 	default_result,
					  XtNlength,	strlen(default_result),
					  XtNwidth,	strlen(default_result) * font_width,
					  XtNfromVert,	promptLabel2,
					  NULL);
    promptDone = XtVaCreateManagedWidget("promptDone", 
					 commandWidgetClass, promptForm, 
					 XtNfromVert,		promptField,
					 NULL);
    promptCancel = XtVaCreateManagedWidget("promptCancel", commandWidgetClass,
					   promptForm, 
					   XtNfromVert,		promptField,
					   XtNfromHoriz,	promptDone,
					   NULL);

    XtAddCallback(promptDone,   XtNcallback, PromptDone,   NULL);
    XtAddCallback(promptCancel, XtNcallback, PromptCancel, NULL);

    SetInSensitive();

    /* Locate window */
    XtTranslateCoords(top, 
		      (Position) 0, 
		      (Position) 0, 
		      &x,
		      &y);
    XtVaSetValues(promptShell, XtNx, x, XtNy, y, NULL);

    XtPopup(promptShell, XtGrabNonexclusive);
    promptCurrentWidgetStore = currentWidget;
    currentWidget = promptField;
    add_widget_to_field(sm_prompt_fld, currentWidget);
    smxaw_warp_mouse(promptDone);
    /* Right trim the default ... */
    while (*(SM_LASTCHAR(default_result)) == ' ')
	*(SM_LASTCHAR(default_result)) = 0; /* Curly! */
}

static Widget create_button ARG3(Widget, parent, CHAR *, label, f_ptr, f)
{
    Widget retval = 
	XtVaCreateManagedWidget(label, commandWidgetClass, parent,
				XtNborderWidth,		1,
				XtNinternalHeight,	0,
				XtNinternalWidth,	0,
				NULL);
    if (f)
	XtAddCallback(retval, XtNcallback, (XtCallbackProc) f, 0);
    return(retval);
}

static Widget create_menu_item ARG3(Widget, parent, CHAR *, label, f_ptr, f)
{
    Widget retval = 
	XtVaCreateManagedWidget(label, smeBSBObjectClass, parent,
				NULL);
    if (f)
	XtAddCallback(retval, XtNcallback, (XtCallbackProc) f, 0);
    return(retval);
}

/*
SM_ACTION_FN(Yes, 	SM_META(SM_CTRL('Y')))
SM_ACTION_FN(No, 	SM_META(SM_CTRL('N')))
SM_ACTION_FN(Cancel, 	SM_CTRL('C'))
*/
SM_ACTION_FN(Prev, 	SM_META('P'))
SM_ACTION_FN(Next, 	SM_META('N'))
SM_ACTION_FN(First, 	SM_META('A'))
SM_ACTION_FN(Last, 	SM_META('E'))
SM_ACTION_FN(Help, 	SM_META('H'))
SM_ACTION_FN(Popup, 	SM_META('M'))
SM_ACTION_FN(Refresh, 	SM_CTRL('L'))

SM_ACTION_FN(Load, 	SM_META(SM_CTRL('l')))
SM_ACTION_FN(Store, 	SM_META(SM_CTRL('s')))
SM_ACTION_FN(StoreAs, 	SM_META(SM_CTRL('a')))
SM_ACTION_FN(Clear, 	SM_META(SM_CTRL('c')))
SM_ACTION_FN(Version, 	SM_CTRL('v'))
SM_ACTION_FN(Quit, 	SM_META('z'))
static void Print() {} /* Do nothing */

SM_ACTION_FN(CopyRecord,	SM_META('C'))       
SM_ACTION_FN(PasteRecord,	SM_META('Y'))      
SM_ACTION_FN(InsertRecord,	SM_META('I'))     
SM_ACTION_FN(DeleteRecord,	SM_META('D'))     
SM_ACTION_FN(ClearToEnd,	SM_CTRL('k'))       
SM_ACTION_FN(ClearRecord,	SM_META('k'))      
SM_ACTION_FN(TransposeLetters,	SM_CTRL('t')) 
SM_ACTION_FN(TransposeWords,	SM_META('t'))   
SM_ACTION_FN(ToggleCase,	SM_CTRL('u'))       
SM_ACTION_FN(FindFirst,		SM_META('s'))        
SM_ACTION_FN(FindLast,		SM_META('l'))         
SM_ACTION_FN(SearchReplace,	SM_META('r'))    
SM_ACTION_FN(ReplaceAll,	SM_META('R'))       
SM_ACTION_FN(Again,		SM_META('S'))            
SM_ACTION_FN(GoToRecord,	SM_META('g'))       
SM_ACTION_FN(Sort,		SM_META('x'))             
SM_ACTION_FN(Where,		SM_META('w'))            
SM_ACTION_FN(EditMaster,	SM_META('L'))       
SM_ACTION_FN(ChangeRecordType,	SM_META('T')) 

static Widget InitTopButtons ARG1(Widget, parent)
{
    Widget	fileButton, fileMenu, editButton, editMenu, topButtons;

    topButtons = 
	XtVaCreateManagedWidget("topButtons", boxWidgetClass, parent, 
				XtNorientation,		XtorientHorizontal,
				XtNborderWidth,		0,
				NULL);

    /* Create the popup file menu: */
    fileButton = XtVaCreateManagedWidget("fileButton", menuButtonWidgetClass,
					 topButtons, 
					 XtNmenuName,	"fileMenu",
					 NULL);

    /* Create the popup edit menu: */
    editButton = XtVaCreateManagedWidget("editButton", menuButtonWidgetClass,
					 topButtons, 
					 XtNmenuName,	"editMenu",
					 NULL);

    create_button(topButtons, "Prev",	(f_ptr) Prev);
    create_button(topButtons, "Next", 	(f_ptr) Next);
    create_button(topButtons, "First", 	(f_ptr) First);
    create_button(topButtons, "Last", 	(f_ptr) Last);
    create_button(topButtons, "Help", 	(f_ptr) Help);
    create_button(topButtons, "Popup", 	(f_ptr) Popup);
    create_button(topButtons, "Refresh",(f_ptr) Refresh);

    fileMenu = XtVaCreatePopupShell("fileMenu", 
				    simpleMenuWidgetClass, fileButton, 
				    NULL);
    create_menu_item(fileMenu,  "Load", 	(f_ptr) Load);
    create_menu_item(fileMenu,  "Store", 	(f_ptr) Store);
    create_menu_item(fileMenu,	"StoreAs",	(f_ptr) StoreAs);
    create_menu_item(fileMenu,  "Clear", 	(f_ptr) Clear);
    create_menu_item(fileMenu,	"Version",	(f_ptr) Version);
    create_menu_item(fileMenu,  "Print", 	(f_ptr) Print);
    create_menu_item(fileMenu,  "Quit", 	(f_ptr) Quit);

    editMenu = XtVaCreatePopupShell("editMenu", 
				    simpleMenuWidgetClass, editButton, 
				    NULL);
    create_menu_item(editMenu,  "CopyRecord", 		(f_ptr) CopyRecord);
    create_menu_item(editMenu,  "PasteRecord", 		(f_ptr) PasteRecord);
    create_menu_item(editMenu,	"InsertRecord",		(f_ptr) InsertRecord);
    create_menu_item(editMenu,  "DeleteRecord", 	(f_ptr) DeleteRecord);
    create_menu_item(editMenu,  "ClearToEnd", 		(f_ptr) ClearToEnd);
    create_menu_item(editMenu,  "ClearRecord", 		(f_ptr) ClearRecord);
    create_menu_item(editMenu,  "TransposeLetters", 	(f_ptr) TransposeLetters);
    create_menu_item(editMenu,  "TransposeWords", 	(f_ptr) TransposeWords);
    create_menu_item(editMenu,  "ToggleCase", 		(f_ptr) ToggleCase);
    create_menu_item(editMenu,	"FindFirst",		(f_ptr) FindFirst);
    create_menu_item(editMenu,  "FindLast", 		(f_ptr) FindLast);
    create_menu_item(editMenu,  "SearchReplace", 	(f_ptr) SearchReplace);
    create_menu_item(editMenu,  "ReplaceAll", 		(f_ptr) ReplaceAll);
    create_menu_item(editMenu,  "Again", 		(f_ptr) Again);
    create_menu_item(editMenu,  "GoToRecord", 		(f_ptr) GoToRecord);
    create_menu_item(editMenu,  "Sort", 		(f_ptr) Sort);
    create_menu_item(editMenu,  "Where", 		(f_ptr) Where);
    create_menu_item(editMenu,  "EditMaster", 		(f_ptr) EditMaster);
    create_menu_item(editMenu,  "ChangeRecordType", 	(f_ptr) ChangeRecordType);
    create_menu_item(editMenu,  "EditMaster", 		(f_ptr) EditMaster);	   
    return(topButtons);
}

void	smxaw_clear ARG1(int, window)
{
    WidgetList		list;
    Cardinal		num;
    struct element	**el;

    unset_cursor();
    XtVaGetValues(userScreen, 
		  XtNchildren, 		&list, 
		  XtNnumChildren, 	&num, 
		  NULL);
    for (; num; num--)
	XtDestroyWidget(list[num-1]);

    /* Clear the xaw_widgetp on fields */
    for (el = displayList; el && *el; el++) {
	if ((*el)->type == FIELD) {
	    SM_FIELD	field = (SM_FIELD)((*el)->ptr);

	    if (field->xaw_widgetp) {
		free(field->xaw_widgetp);
		field->xaw_widgetp = NULL;
	    }
	}
    }

    /* Delete the displayList array */
    sm_delete_data((SM_DATA) displayList);
    displayList = NULL;
    smxaw_form_realised = 0;
}

void	smxaw_enq ARG3(int, window, int *, row, int *, col)
{
    *row = NUM_LINES_ON_FORM + 1;
    *col = FORM_WIDTH;
}

void	smxaw_enq_screen ARG2(int *, row, int *, col)
{
    *row = NUM_LINES_ON_FORM + 1;
    *col = FORM_WIDTH;
}

void	smxaw_goto ARG3(int, window, int, row, int, col)
{
    smxaw_row = row;
    smxaw_col = col;
}

void	smxaw_enh ARG2(int, window, ENH, enhancement)
{
    int	i, mapped_enh = 0;
}

void	smxaw_puts ARG2(int, window, CHAR *, s)
{
    int		y, x, maxlen;
    CHAR	saved;

    /* create_label(s, userScreen, lastvwidget, lasthwidget, 0); */
}

void smxaw_meta_on()
{
    XtVaSetValues(MetaWidget, XtNlabel, "M-", NULL);
}

void smxaw_meta_off()
{
    XtVaSetValues(MetaWidget, XtNlabel, "  ", NULL);
}

void smxaw_paint_skey ARG3(int, i, char *, upper, char *, lower)
{
    char	buf[20];

    sprintf(buf, "%s\n%s", upper, lower);
    XtVaSetValues(key[i], XtNlabel, buf, NULL);
}

int	smxaw_enq_mouse ARG3(int *, row, int *, col, int *, time_delta)
{
    *row = smxaw_mouse_row;
    *col = smxaw_mouse_col;
    return(smxaw_button);
}
	
void	smxaw_refresh()
{
}

/*
 * Right trim a column
 */
int smxaw_maxcol ARG2(int, window, int, col_desired)
{
    return(col_desired);
}

void smxaw_msg ARG1(char *, s)
{
    XtVaSetValues(MsgWidget, XtNlabel, s, NULL);
}

void smxaw_main_loop ARG1(f_ptr, got_a_command)
{
    XEvent	event;

    smxaw_got_a_command = got_a_command;
    /* XtAppMainLoop(appctx); /* sits here till quit time */
    while (top) {
	XtAppNextEvent(appctx, &event);
	XtDispatchEvent(&event);
    }
}

static int confirmFlag, confirmResult;

static void ConfirmYes()
{
    confirmResult = SM_YES_C;
    confirmFlag = 0;
}

static void ConfirmNo()
{
    confirmResult = SM_NO_C;
    confirmFlag = 0;
}


static void ConfirmCancel()
{
    confirmResult = SM_CANCEL_C;
    confirmFlag = 0;
}

int smxaw_confirm ARG1(char *, s)
{
    XEvent	event;
    Widget 	confirmNo, confirmCancel, labelWidget;
    CHAR	*label;
    Widget	confirmShell, confirmWidget, confirmYesWidget;
    Position	x, y;
    Dimension	width, height;
    
    confirmShell =  XtVaCreatePopupShell("confirm", transientShellWidgetClass,
					 top, NULL);
    confirmWidget = XtVaCreateManagedWidget("confirmWidget", dialogWidgetClass,
					    confirmShell, 
					    XtNlabel, s,
					    NULL);
    confirmYesWidget =    
	XtVaCreateManagedWidget("confirmYes", commandWidgetClass,
				confirmWidget, XtNlabel, "Yes", NULL);
    confirmNo =     
	XtVaCreateManagedWidget("confirmNo", commandWidgetClass,
				confirmWidget, XtNlabel, "No", NULL);
    confirmCancel = 
	XtVaCreateManagedWidget("confirmCancel", commandWidgetClass, 
				confirmWidget, XtNlabel, "Cancel", NULL);

    labelWidget = XtNameToWidget(confirmWidget, "label");
    XtVaSetValues(labelWidget,
		  XtNinternalHeight,	0,
		  XtNinternalWidth,	0,
		  XtNbottomMargin,	0,
		  XtNtopMargin,		0,
		  XtNleftMargin,	0,
		  XtNrightMargin,	0,
		  XtNjustify,		XtJustifyLeft,  
		  NULL);

    XtAddCallback(confirmYesWidget, XtNcallback, ConfirmYes,    NULL);
    XtAddCallback(confirmNo, 	    XtNcallback, ConfirmNo,     NULL);
    XtAddCallback(confirmCancel,    XtNcallback, ConfirmCancel, NULL);

    /*XtVaGetValues(top, XtNwidth, &width, XtNheight, &height, NULL); */
    XtTranslateCoords(top, (Position) 0 /*width/2*/, (Position) 0 /*height/2*/, &x,&y);
    XtVaSetValues(confirmShell, XtNx, x, XtNy, y, NULL);
    /**/
    SetInSensitive();
    XtPopup(confirmShell, XtGrabNonexclusive);
    confirmFlag = 1;
    confirmResult = 0;
    smxaw_warp_mouse(confirmYesWidget);
    while (confirmFlag) { /* wait for response */
	XtAppNextEvent(appctx, &event);
	XtDispatchEvent(&event);
    }
    XtPopdown(confirmShell);
    SetSensitive();
    XtDestroyWidget(confirmShell);
    return(confirmResult);
}

static void ErrorContinue()
{
    errorFlag = 0;
}

void smxaw_error ARG1(char *, s) 
{
    XEvent	event;
    Widget	errorWidget, errorContinue, labelWidget, errorShell;
    CHAR	*label;
    Position	x, y;
    
    errorShell =  XtVaCreatePopupShell("Error", transientShellWidgetClass,
				       top, NULL);
    errorWidget = XtVaCreateManagedWidget("errorWidget", dialogWidgetClass,
					  errorShell, 
					  XtNlabel, s, 
					  NULL);
    errorContinue = 
	XtVaCreateManagedWidget("errorContinue", 
				commandWidgetClass, errorWidget, NULL);
    XtAddCallback(errorContinue, XtNcallback, ErrorContinue, NULL);
    labelWidget = XtNameToWidget(errorWidget, "label");
    XtVaSetValues(labelWidget,
		  XtNinternalHeight,	0,
		  XtNinternalWidth,	0,
		  XtNbottomMargin,	0,
		  XtNtopMargin,		0,
		  XtNleftMargin,	0,
		  XtNrightMargin,	0,
		  XtNjustify,		XtJustifyLeft,  
		  NULL);

    XtTranslateCoords(top, (Position) 0, (Position) 0, &x,&y);
    XtVaSetValues(errorShell, XtNx, x, XtNy, y, NULL);

    SetInSensitive();
    XtPopup(errorShell, XtGrabNonexclusive);
    errorFlag = 1;
    smxaw_warp_mouse(errorContinue);
    while (errorFlag) { /* wait for response */
	XtAppNextEvent(appctx, &event);
	XtDispatchEvent(&event);
    }
    XtPopdown(errorShell);
    XtDestroyWidget(errorShell);
    SetSensitive();
}

void smxaw_beep() {}
int	smxaw_popup ARG4(int, num_rows, int, num_cols, int, row, int, col) {}
void	smxaw_delwin ARG1(int, window) {}

static int intersects ARG1(ELEMENT, new_element)
{
    ELEMENT	*el = displayList;

    for (;el && *el; el++) {
	if ((*el)->row == new_element->row)
	    if ((((*el)->col >= new_element->col) && 
		 ((*el)->col <  new_element->col + new_element->len)) ||
		((new_element->col >= (*el)->col) &&
		 (new_element->col <  (*el)->col + (*el)->len)))
		return(1);
    }
    return(0);
}

void smxaw_create_bg ARG1(SM_BACKGROUND, bg)
{
    INDEX	new = MAX_INDEX;
    ELEMENT	new_element;

    new_element = (ELEMENT) xmalloc((INDEX) sizeof(struct element));
    new_element->type = BACKGROUND;
    new_element->row = bg->row;
    new_element->col = 0;
    new_element->len = strlen(bg->text->str);
    new_element->ptr = bg;
    if (intersects(new_element))
	/* Throw away any that overlap (master screen fields) */
	free(new_element);
    else {
	displayList = (ELEMENT *) sm_increase_size((SM_DATA) displayList, 
						   &new);
	displayList[new] = new_element;
    }
}

void smxaw_create_fld ARG1(SM_FIELD, field)
{
    INDEX	new = MAX_INDEX;
    ELEMENT	new_element;
    int		row = field->row;
    SM_SCREEN	screen = field->parent;
    
    /* This also propagates scrolling fields */
    do {
	new_element = (ELEMENT) xmalloc((INDEX) sizeof(struct element));
	new_element->type = FIELD;
	new_element->row = row;
	new_element->col = field->col;
	new_element->len = abs(field->len);
	new_element->ptr = (void *) field;
	if (intersects(new_element))
	    /* Throw away any that overlap (master screen fields) */
	    free(new_element);
	else {
	    displayList = (ELEMENT *) sm_increase_size((SM_DATA) displayList, 
						       &new);
	    displayList[new] = new_element;
	}
    } while (screen->scrolling && 
	     (field->row >= screen->scrolling - 1) &&
	     (row++ < NUM_LINES_ON_FORM));
}

void smxaw_set_fld ARG2(SM_FIELD, fld, char *, val)
{
    Widget	w = field_to_widget(fld);

    if (w)
	XtVaSetValues(w, XtNstring, val, NULL);
}

static int smxaw_comp ARG2(const void *, a1, const void *, a2)
{
    if ((*(ELEMENT *)a1)->row == (*(ELEMENT *)a2)->row)
	return ((*(ELEMENT *)a1)->col - (*(ELEMENT *)a2)->col);
    else
	return((*(ELEMENT *)a1)->row - (*(ELEMENT *)a2)->row);
}

void smxaw_realise_form()
{
    int			number, row, base = 0, blank_row = 1;
    struct element	**el;
    char		*buf   = alloca(MAX_FIELD_LEN);
    char		*blank = alloca(MAX_FIELD_LEN);
    Widget		lastvwidget = 0, lasthwidget = 0;

    if ((displayList == NULL) || (*displayList == NULL) || smxaw_form_realised)
	return;

    /* Sort the form entities ... */
    /* Count them ... */
    for (number = 0; displayList[number]; number++)
	;
    qsort((CHAR *)displayList, number, sizeof(ELEMENT *), smxaw_comp);

    base = 0;
    *buf = 0;
    row = 0;
    /* Create widgets ... */
    for (el = displayList; *el; el++) {
	/* Flush out any labels */
	if (*buf && ((*el)->row > row)) {
	    lastvwidget = create_label(buf, userScreen, lastvwidget, lasthwidget);
	    *buf = 0;
	    row++;
	    blank_row = 1;
	    base = 0;
	    lasthwidget = 0;
	}

	/* Fill in any blank rows */
	while (row < (*el)->row) {
	    if (blank_row)
		lastvwidget = create_label(" ", userScreen, lastvwidget, 0);
	    else
		lastvwidget = lasthwidget;
	    row++;
	    base = 0;
	    lasthwidget = 0;
	    blank_row = 1;
	}

	while (strlen(buf) + base < (*el)->col)
	    strcat(buf, " ");

	if ((*el)->type == BACKGROUND) {
	    strcat(buf, ((SM_BACKGROUND) (*el)->ptr )->text->str);
	} else { /* FIELD */
	    SM_FIELD	field = (SM_FIELD)((*el)->ptr);
	    SM_SCREEN	screen = field->parent;

	    /* Flush out any labels ... */
	    if (*buf)
		lasthwidget = create_label(buf, userScreen, lastvwidget, lasthwidget);
	    memset(blank, ' ', MAX_FIELD_LEN);
	    blank[(*el)->len] = 0;
	    strncpy(blank, field->c_value, 
		    strlen(field->c_value));
	    lasthwidget = create_field(field->field_name,
				       blank, 
				       userScreen,
				       lastvwidget,
				       lasthwidget);
	    add_widget_to_field(field, lasthwidget);
	    base += (strlen(buf) + (*el)->len);
	    *buf = 0;
	    blank_row = 0;
	}
    }

    /* Fill up terminal rows */
    while (row < NUM_LINES_ON_FORM) {
	if (blank_row)
	    lastvwidget = create_label(" ", userScreen, lastvwidget, 0);
	else
	    lastvwidget = lasthwidget;
	row++;
	blank_row = 1;
	lasthwidget = 0;
    }

    smxaw_form_realised = 1;
}

void smxaw_down_tools()
{
/*    XtUnmanageChild(top);*/
}

void smxaw_up_tools()
{
/*    XtManageChild(top);*/
}

VOID smxaw_getyx ARG3(int, window, int *, row, int *, col)
{
    int pos;

    *row = *col = 0;
    /* We're not interested in popups - when window != 0 */
    if ((window == 0) && currentWidget) {
	*row = currentField->row;
	*col = currentField->col + XawTextGetInsertionPoint(currentWidget);
    }
}

static XtActionsRec actions[] = {
    /* f1 has to be the first for the sake of the XtAddCallback
       in InitButtons */
    {"f1",		f1}, 
    {"f2",		f2},
    {"f3",		f3},
    {"f4",		f4},
    {"f5",		f5},
    {"f6",		f6},
    {"f7",		f7},
    {"f8",		f8},
    {"ProcessKey", 	ProcessKey},
    {"ProcessB1", 	ProcessB1},
    {"InsertCallback",	InsertCallback},
    {"ErrorContinue",	ErrorContinue},
    {"SelectDone",	SelectDone},
    {"SelectCancel",	SelectCancel},
};

static Widget InitButtons ARG2(Widget, parent, Widget, above)
{
    int 		j;
    Widget 		lasthwidget = 0;
    char 		wname[20], label[20];

    for (j = 0; j < 8; j++) {
	sprintf(label, "button %d\nlower   ", j);
	sprintf(wname, "button%d", j);
	lasthwidget = key[j] =
	    XtVaCreateManagedWidget(wname, commandWidgetClass, parent,
				    XtNfromVert, 	above,
				    XtNfromHoriz, 	lasthwidget,
				    XtNlabel, 		label,
				    NULL);
	    XtAddCallback(lasthwidget, XtNcallback,  (XtCallbackProc) actions[j].proc, 0);
	
	sprintf(label, "%d", j+1);
	lasthwidget = create_label(label, parent, above, lasthwidget);

	if (j == 3) {
	    InsWidget = 
		XtVaCreateManagedWidget("Insert", commandWidgetClass, parent, 
					XtNfromVert, 		above,
					XtNfromHoriz, 		lasthwidget,
					XtNinternalWidth,	1,
					XtNlabel, 		"I",
					NULL);
	    XtAddCallback(InsWidget, XtNcallback, InsertCallback, 0);
	    
	    lasthwidget = MetaWidget = 
		XtVaCreateManagedWidget("Meta", labelWidgetClass, parent, 
					XtNfromVert, 		above,
					XtNfromHoriz, 		InsWidget,
					XtNlabel, 		"  ",
					NULL);
	    /*
	    lasthwidget = 
		XtVaCreateManagedWidget("X", labelWidgetClass, parent,
					XtNfromVert, 		InsWidget,
					XtNfromHoriz, 		lasthwidget,
					XtNlabel, 		"00",
					NULL);
	    
	    lasthwidget = 
		XtVaCreateManagedWidget("Y", labelWidgetClass, parent, 
					XtNfromVert, 		InsWidget,
					XtNfromHoriz, 		lasthwidget,
					XtNlabel, 		"00",
					NULL);
            */
	}
    }
    
    return lasthwidget;
}

static Widget InitForm ARG1(Widget, parent)
{
    int		n;
    Widget 	lastvwidget = 0;
    char	blank[MAX_FIELD_LEN];

    /* At least one item on each line! */
    memset(blank, 'b', MAX_FIELD_LEN);
    blank[FORM_WIDTH] = 0;
    for (n = 0; n < NUM_LINES_ON_FORM; n++)
	lastvwidget = create_label(blank, parent, lastvwidget, 0);

    return(lastvwidget);
}

Widget InitMsg ARG2(Widget, parent, Widget, above)
{
    Widget 	lasthwidget = 0;
    char	blank[MAX_FIELD_LEN];

    memset(blank, 'b', MAX_FIELD_LEN);
    blank[FORM_WIDTH] = 0;
    
    /* Max length is set at creation so use very long string ... bug in Xaw? */
    MsgWidget = 
	XtVaCreateManagedWidget(blank, labelWidgetClass, parent,
				XtNfromVert,		above,
				/*XtNjustify,		XtJustifyLeft,  */
				NULL);
    return(MsgWidget);
}

void	smxaw_term()
{
    if (top)
	XtDestroyWidget(top);
    top = NULL;
}

void	smxaw_init ARG3(int, doit, int, argc, CHAR **, argv)
{
    int		i;
    Widget 	lasthwidget = 0, lastvwidget = 0;
    XFontStruct	*fs;
    Dimension	borderWidth; 
    Position	bottomMargin, topMargin;
    int		defaultDist;

    if (doit) {
	top = XtAppInitialize(&appctx, argv[0], NULL, 0,
			      &argc, argv, fallback_resources, NULL, 0);
	dpy = XtDisplay(top);

	form0 = /* base form for everything */
	    XtVaCreateManagedWidget("form0", formWidgetClass, top, 
				    XtNfromVert, 	topButtons,
				    NULL);
	
	topButtons = InitTopButtons(form0);

	userScreen = /* user screen */
	    XtVaCreateManagedWidget("userScreen", formWidgetClass, form0, 
				    XtNdefaultDistance, 	1,
				    XtNfromVert,		topButtons,
				    NULL);

	lastvwidget = InitForm(userScreen); 

	/* Now get the font metrics ... */
	XtVaGetValues(lastvwidget, 
		      XtNfont, 			&fs,
		      NULL);
	font_width = XTextWidth(fs, "W", 1);
	lastvwidget = create_field("Blank", "BLANK", userScreen, 0, 0);
	XtVaGetValues(userScreen,
		      XtNdefaultDistance,	&defaultDist,
		      NULL);
	XtVaGetValues(lastvwidget,
		      XtNbottomMargin,		&bottomMargin,
		      XtNtopMargin,		&topMargin,
		      XtNborderWidth,		&borderWidth,
		      NULL);
	
	font_height = fs->ascent + fs->descent + borderWidth * 2 +
	    bottomMargin + topMargin + defaultDist;
			  
	bottomButtons = /* button box */
	    XtVaCreateManagedWidget("bottomButtons", formWidgetClass, form0, 
				    XtNdefaultDistance,	1,
				    XtNfromVert, 	userScreen,
				    NULL);
	lastvwidget = NULL;
	
	lastvwidget = MsgWidget = InitMsg(bottomButtons, lastvwidget);
	/* init the message area */
	
	lastvwidget = InitButtons(bottomButtons, lastvwidget);
	/* init command buttons */

	XtAppAddActions(appctx, actions, XtNumber(actions));
	
	XtRealizeWidget(top);

	/* The reason this is here instead of in smxaw_confirm is
	   that setting XtN[xy] if top has not been moved takes about
	   5 seconds! Bug in Sun's OW 3? Having the popup always come
	   up in the same place is a) nice, as the user can put it
	   somewhere and it always pops up there, and b) not nice as if
	   top is moved, the popup does not move with it.
	*/

	top_of_wstack = 0;
    }
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
